﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;

namespace EndlessGalaxy
{
    public class Globals
    {
        public static List<Skybox> systems;
        public static Dictionary<string, SuperCargo> materials;
        public static Random randint;
        public static double solarmass = 1989100000000000000000000000.0;
        public static Form1 MainWindow;
        public static List<Skybox> knownsystems;
        public static int currentsystem;
        public static List<string> usednames;
        public static List<string> unusednames;
        public static string promptname;

        public Globals(Form1 Main)
        {
            randint = new Random();
            materials = new Dictionary<string, SuperCargo>();
            SuperCargo nullref = new SuperCargo();
            nullref.name = "NullRef";
            nullref.attributes = new List<string>() { "NullRef" };
            nullref.formula = "NullRef";
            nullref.phase = Cargo.Phases.other;
            materials.Add("NullRef", nullref);
            usednames = new List<string>();
            unusednames = new List<string>();
            Ship.ShipTypes = new Dictionary<string, Ship>();
            ReadRaws();
            MainWindow = Main;
            currentsystem = 0;
            knownsystems = new List<Skybox>();
        }

        public static List<Cargo> Copy(List<Cargo> incoming)
        {
            List<Cargo> newbie = new List<Cargo>();
            foreach (Cargo thing in incoming)
            {
                newbie.Add(thing);
            }
            return newbie;
        }
        public static Cargo Copy(Cargo incoming)
        {
            Cargo thing = new Cargo();
            thing.amount = incoming.amount;
            thing.density = incoming.density;
            thing.name = incoming.name;
            thing.phase = incoming.phase;
            return thing;
        }

        /// <summary>
        /// Outputs the offset from the top-left of the larger rectange to place the smaller one.
        /// </summary>
        /// <param name="small">The width or height of the smaller rectangle.</param>
        /// <param name="large">The width or height of the larger rectangle.</param>
        /// <returns>The amount of offset required to center it.</returns>
        public static int CenterRectangle(int small, int large)
        {
            if ((small >= large) || (small <= 0) || (large <= 0))
            {
                return 0;
            }
            return (large / 2) - (small / 2);
        }

        public static Point MovePointTowards(Point a, Point b, double distance)
        {
            double pxRes = b.X - a.X;
            double pyRes = b.Y - a.Y;
            double angle = 0.0;
            // Calculate the angle
            if (pxRes == 0.0)
            {
                if (pxRes == 0.0)
                    angle = 0.0;
                else if (pyRes > 0.0) angle = System.Math.PI / 2.0;
                else
                    angle = System.Math.PI * 3.0 / 2.0;
            }
            else if (pyRes == 0.0)
            {
                if (pxRes > 0.0)
                    angle = 0.0;
                else
                    angle = System.Math.PI;
            }
            else
            {
                if (pxRes < 0.0)
                    angle = System.Math.Atan(pyRes / pxRes) + System.Math.PI;
                else if (pyRes < 0.0) angle = System.Math.Atan(pyRes / pxRes) + (2 * System.Math.PI);
                else
                    angle = System.Math.Atan(pyRes / pxRes);
            }
            return new Point(Convert.ToInt32(a.X + distance * Math.Cos(angle)), Convert.ToInt32(a.Y + distance * Math.Sin(angle)));
        }

        public static int Distance(Point a, Point b)
        {
            return Convert.ToInt32(Math.Sqrt(Math.Pow(a.X - b.X, 2) + Math.Pow(a.Y - b.Y, 2)));
        }

        public static void ReadRaws()
        {
            string[] raws = Directory.GetFiles("raws", "*.txt");
            Array.Sort(raws);
            foreach (string filename in raws)
            {
                string line;
                System.IO.StreamReader file = new System.IO.StreamReader(filename);
                if (filename == "raws\\systemNames.txt")
                {
                    while ((line = file.ReadLine()) != null)
                    {
                        unusednames.Add(line);
                    }
                    file.Close();
                }
                else
                {
                    SuperCargo thing = new SuperCargo();
                    bool complete = false;
                    string nametag = "";
                    double[] densities = new double[3];
                    while ((line = file.ReadLine()) != null)
                    {
                        while ((line.Contains('[')) && (line.Contains(']')))
                        {
                            int i1 = line.IndexOf('[');
                            int i2 = line.IndexOf(':');
                            int i3 = line.IndexOf(']');
                            string tagtype = "";
                            string tagvalue = "";
                            if (i1 < i3)
                            {
                                if (i2 > -1)
                                {
                                    tagtype = line.Substring(i1 + 1, i2 - i1 - 1);
                                    tagvalue = line.Substring(i2 + 1, i3 - i2 - 1);
                                }
                                else
                                {
                                    tagtype = line.Substring(i1 + 1, i3 - i1 - 1);
                                }
                                switch (tagtype)
                                {
                                    case "MATERIAL":
                                        if (complete)
                                        {
                                            materials.Add(thing.name, thing);
                                        }
                                        complete = false;
                                        thing = new SuperCargo();
                                        nametag = tagvalue;
                                        break;
                                    case "NAME":
                                        thing.name = tagvalue;
                                        complete = CheckCompletion(thing);
                                        break;
                                    case "DENSITY":
                                        int r1 = tagvalue.IndexOf(':');
                                        int r2 = tagvalue.IndexOf(':', r1 + 1);
                                        if ((r1 > 0) && (r2 > r1))
                                        {
                                            string d1 = tagvalue.Substring(0, r1);
                                            string d2 = tagvalue.Substring(r1 + 1, (r2 - (r1 + 1)));
                                            string d3 = tagvalue.Substring(r2 + 1);
                                            Double.TryParse(d1, out thing.densityS);
                                            Double.TryParse(d2, out thing.densityL);
                                            Double.TryParse(d3, out thing.densityG);
                                            complete = CheckCompletion(thing);
                                        }
                                        break;
                                    case "MELT_BOIL_TEMP":
                                        r1 = tagvalue.IndexOf(':');
                                        if (r1 > 0)
                                        {
                                            string d1 = tagvalue.Substring(0, r1);
                                            string d2 = tagvalue.Substring(r1 + 1);
                                            Double.TryParse(d1, out thing.meltPoint);
                                            Double.TryParse(d2, out thing.boilPoint);
                                            complete = CheckCompletion(thing);
                                        }
                                        break;
                                    case "STP_PHASE":
                                        switch (tagvalue)
                                        {
                                            case "S":
                                                thing.phase = Cargo.Phases.solid;
                                                break;
                                            case "L":
                                                thing.phase = Cargo.Phases.liquid;
                                                break;
                                            case "G":
                                                thing.phase = Cargo.Phases.gas;
                                                break;
                                        }
                                        complete = CheckCompletion(thing);
                                        break;
                                    case "ELEMENTAL":
                                        thing.elemental = true;
                                        r1 = tagvalue.IndexOf(':');
                                        r2 = tagvalue.IndexOf(':', r1 + 1);
                                        if ((r1 > 0) && (r2 > r1))
                                        {
                                            string d2 = tagvalue.Substring(r1 + 1, (r2 - (r1 + 1)));
                                            string d3 = tagvalue.Substring(r2 + 1);
                                            Int32.TryParse(d2, out thing.elementnum);
                                            Int32.TryParse(d3, out thing.stableneutrons);
                                            tagvalue = tagvalue.Substring(0, r1);
                                        }
                                        goto case "COMPOUND";
                                    case "COMPOUND":
                                        thing.formula = tagvalue;
                                        complete = CheckCompletion(thing);
                                        break;
                                    default:
                                        thing.attributes.Add(tagtype);
                                        complete = CheckCompletion(thing);
                                        break;
                                }
                            }
                            line = line.Remove(i1, i3 - i1 + 1);
                        }
                    }
                    file.Close();
                    if (complete)
                    {
                        materials.Add(thing.name, thing);
                    }
                }
            }
        }

        public static bool CheckCompletion(SuperCargo thing)
        {
            if (thing.name == null)
            {
                return false;
            }
            else
            {
                if (thing.name.Length == 0)
                {
                    return false;
                }
            }
            if ((thing.densityG <= 0) || (thing.densityL <= 0) || (thing.densityS <= 0))
            {
                return false;
            }
            if ((thing.meltPoint <= 0) || (thing.boilPoint <= 0))
            {
                return false;
            }
            if (thing.formula == null)
            {
                return false;
            }
            else
            {
                if (thing.formula.Length == 0)
                {
                    return false;
                }
            }
            return true;
        }

        public static int takeCargo(string cargoName, double amount, List<Cargo> from, List<Cargo> to)
        {
            int result = 0; //0 for total success, 1 for partial, 2 for total failure
            foreach (Cargo thing in from)
            {
                if (thing.name == cargoName)
                {
                    if (thing.amount < amount)
                    {
                        amount = thing.amount;
                        result = 1;
                    }
                    Cargo.GiveCargo(cargoName, amount, to);
                    return result;
                }
            }
            return 2;
        }

        public static string GetRandomName(Image systemImage)
        {
            if (unusednames.Count < 1)
            {
                return PromptName(systemImage);
            }
            else
            {
                int i = randint.Next(0, unusednames.Count);
                string name = unusednames[i];
                usednames.Add(unusednames[i]);
                unusednames.RemoveAt(i);
                return name;
            }
        }

        public static string PromptName(Image pic)
        {
            promptname = "";
            NamePrompt np = new NamePrompt(pic);
            while (promptname.Length <= 2)
            {
                np.ShowDialog();
            }
            usednames.Add(promptname);
            File.AppendAllText("raws\\systemNames.txt", promptname + Environment.NewLine);
            return promptname;
        }

        public static char IncrementLetter(char inp)
        {
            switch (inp)
            {
                case '-':
                    return 'a';
                case 'a':
                    return 'b';
                case 'b':
                    return 'c';
                case 'c':
                    return 'd';
                case 'd':
                    return 'e';
                case 'e':
                    return 'f';
                case 'f':
                    return 'g';
                case 'g':
                    return 'h';
                case 'h':
                    return 'i';
                case 'i':
                    return 'j';
                case 'j':
                    return 'k';
                case 'k':
                    return 'l';
                case 'l':
                    return 'm';
                case 'm':
                    return 'n';
                case 'n':
                    return 'o';
                case 'o':
                    return 'p';
                case 'p':
                    return 'q';
                case 'q':
                    return 'r';
                case 'r':
                    return 's';
                case 's':
                    return 't';
                case 't':
                    return 'u';
                case 'u':
                    return 'v';
                case 'v':
                    return 'w';
                case 'w':
                    return 'x';
                case 'x':
                    return 'y';
                case 'y':
                    return 'z';
                case 'z':
                    return 'a';
                default:
                    return inp;
            }
        }
        public static string IncrementRomans(string inp)
        {
            switch (inp)
            {
                case "":
                    return "I";
                case "I":
                    return "II";
                case "II":
                    return "III";
                case "III":
                    return "IV";
                case "IV":
                    return "V";
                case "V":
                    return "VI";
                case "VI":
                    return "VII";
                case "VII":
                    return "VIII";
                case "VIII":
                    return "IX";
                case "IX":
                    return "X";
                case "X":
                    return "XI";
                case "XI":
                    return "XII";
                case "XII":
                    return "XIII";
                case "XIII":
                    return "XIV";
                case "XIV":
                    return "XV";
                case "XV":
                    return "XVI";
                case "XVI":
                    return "XVII";
                case "XVII":
                    return "XVIII";
                case "XVIII":
                    return "XIX";
                case "XIX":
                    return "XX";
                default:
                    return "?";
            }
        }

        public static double DistanceFromCenter(Point location)
        {
            return Math.Sqrt(Math.Pow(300 - location.X, 2) + Math.Pow(300 - location.Y, 2));
        }

        public static string getData(Planet here)
        {
            string data = "Object type: " + Enum.GetName(typeof(Planet.worldType), here.type);
            data += "\nName: " + here.name;
            //data += "\nMade of:";
            data += "\nMass: " + here.Mass() + " tonnes";
            foreach (Cargo item in here.composition)
            {
                //data += "\n" + item.name + ": " + item.amount + " tonnes";
            }
            if (here.atmosphere.Count > 0)
            {
                data += "\nAtmosphere:";
                List<Cargo> breather = here.AtmoBottom();
                List<Cargo> br2 = new List<Cargo>();
                double atmomass = 0.0;
                foreach (Cargo item in breather)
                {
                    atmomass += item.amount;
                }
                foreach (Cargo item in breather)
                {
                    br2.Add(new Cargo(item, ((item.amount / atmomass) * 100)));
                }
                foreach (Cargo item in br2)
                {
                    if (item.amount < 0.005)
                    {
                        if (item.amount > 0.000001)
                        {
                            data += "\n" + item.name + ": Trace amounts";
                        }
                        //else
                        //{
                        //data += "\nMicroscopic traces of " + item.name;//For debug purposes.  Will re-enable with tech trees.
                        //}
                    }
                    else
                    {
                        data += "\n" + item.name + ": " + Math.Round(item.amount, 2) + "%";
                    }
                }
            }
            if (here.surface.Count > 0)
            {
                data += "\nSurface contains:";
                foreach (Cargo item in here.surface)
                {
                    data += "\n" + item.name + ": " + item.amount + " tonnes";
                }
            }
            data += "\n\nSurface Materials Available:";
            List<Cargo> skimmer = here.CompositionTop();
            List<Cargo> Sk2 = new List<Cargo>();
            double surfacemass = 0.0;
            foreach (Cargo item in skimmer)
            {
                surfacemass += item.amount;
            }
            foreach (Cargo item in skimmer)
            {
                Sk2.Add(new Cargo(item, ((item.amount / surfacemass) * 100)));
            }
            foreach (Cargo item in Sk2)
            {
                if (item.amount < 0.005)
                {
                    if (item.amount > 0.000001)
                    {
                        data += "\n" + item.name + ": Trace amounts";
                    }
                    //else
                    //{
                    //    data += "\nMicroscopic traces of " + item.name;
                    //}
                }
                else
                {
                    data += "\n" + item.name + ": " + Math.Round(item.amount, 2) + "%";
                }
            }
            if ((here.type != Planet.worldType.gasGiant) && (here.type != Planet.worldType.bHole) && (here.type != Planet.worldType.star) &&
                    (here.type != Planet.worldType.neutronstar))
            {
                data += "\n\nRadiation levels: " + (here.radiation - 273.15);
                data += "\nAverage surface temp: " + (here.aveTemp - 273.15);
            }
            if ((here.type == Planet.worldType.bHole) || (here.type == Planet.worldType.star) ||
                (here.type == Planet.worldType.neutronstar))
            {
                data += "\n\nRadiation levels: " + here.radiation;
            }
            return data;
        }
    }
}